package com.dmbjz.point.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.dmbjz.common.constant.ApiConstant;
import com.dmbjz.common.constant.RedisKeyConstant;
import com.dmbjz.common.exception.ParameterException;
import com.dmbjz.common.model.domain.ResultInfo;
import com.dmbjz.common.model.entity.DinerPoints;
import com.dmbjz.common.model.vo.DinerPointsRankVO;
import com.dmbjz.common.model.vo.ShortDinerInfo;
import com.dmbjz.common.model.vo.SingInDinerInfo;
import com.dmbjz.common.utils.AssertUtil;
import com.dmbjz.point.dao.DinerPointsDao;
import com.google.common.collect.Lists;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.atomic.LongAdder;

@Service
@Transactional(rollbackFor = Exception.class)
public class DinerPointsService {


    @Autowired
    private DinerPointsDao dinerPointsMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${service.name.ms-oauth-service}")
    private String oauthServerName;
    @Value("${service.name.ms-diner-service}")
    private String dinersServerName;
    // 排行榜 TOPN
    private static final int TOPN = 20;


    /**
     * 添加积分
     *
     * @param dinerId 食客ID
     * @param points  积分
     * @param types   类型 0=签到，1=关注好友，2=添加Feed，3=添加商户评论
     */
    public void addPoints(Integer dinerId, Integer points, Integer types) {
        // 基本参数校验
        AssertUtil.isTrue(dinerId == null || dinerId < 1, "食客不能为空");
        AssertUtil.isTrue(points == null || points < 1, "积分不能为空");
        AssertUtil.isTrue(types == null, "请选择对应的积分类型");

        // 插入数据库
        DinerPoints dinerPoints = new DinerPoints();
        dinerPoints.setFkDinerId(dinerId);
        dinerPoints.setPoints(points);
        dinerPoints.setTypes(types);
        dinerPointsMapper.save(dinerPoints);

        // 将积分保存到 Redis
        redisTemplate.opsForZSet().incrementScore(
                RedisKeyConstant.diner_points.getKey(), dinerId, points);
    }


    /*查询排行榜前20与登录用户的排名 Redis版*/
    public List<DinerPointsRankVO> findDinerPointRankRedis(String accessToken) {

        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);                                               //获取用户登录信息
        Set<ZSetOperations.TypedTuple<Integer>> voSet = redisTemplate.opsForZSet().reverseRangeWithScores(
                RedisKeyConstant.diner_points.getKey(), 0, 19);//返回倒序前20的
        if (CollectionUtil.isEmpty(voSet)) {
            return new ArrayList<>(1);
        }


        List<Integer> rankDinerList = Lists.newArrayList();                       //创建用户ID集合
        Map<Integer, DinerPointsRankVO> ranksMap = new LinkedHashMap<>();        //将List构建Map，key用户ID，Value积分信息

        /*循环处理排行榜，添加到LinkHashMap中保证顺序*/
        LongAdder adder = new LongAdder();
        adder.increment();                                                      //初始化排名为1
        for (ZSetOperations.TypedTuple<Integer> temp : voSet) {
            Integer dinerId = temp.getValue();                                //用户ID
            int points = temp.getScore().intValue();                         //积分
            rankDinerList.add(dinerId);                                     //添加用户ID到集合中

            DinerPointsRankVO dinerPointsRankVO = new DinerPointsRankVO();
            dinerPointsRankVO.setId(dinerId);
            dinerPointsRankVO.setRanks(adder.intValue());
            dinerPointsRankVO.setTotal(points);
            ranksMap.put(dinerId, dinerPointsRankVO);

            adder.increment();   //排名加1
        }

        /*远程调用，请求用户信息*/
        ResultInfo resultInfo = restTemplate.getForObject(dinersServerName + "findByIds?ids={ids}",
                ResultInfo.class,
                StrUtil.join(",", rankDinerList));
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            throw new ParameterException(resultInfo.getCode(), resultInfo.getMessage());
        }
        List<LinkedHashMap> dataMaps = (List<LinkedHashMap>) resultInfo.getData();
        /*处理用户头像信息*/
        for (LinkedHashMap tempMap : dataMaps) {
            ShortDinerInfo shutInfo = BeanUtil.fillBeanWithMap(tempMap, new ShortDinerInfo(), false);
            DinerPointsRankVO rankVo = ranksMap.get(shutInfo.getId());
            rankVo.setAvatarUrl(shutInfo.getAvatarUrl());
            rankVo.setNickname(shutInfo.getNickname());
        }

        /*判断用户是否在排行榜中*/
        if (ranksMap.containsKey(dinerInfo.getId())) {
            DinerPointsRankVO userVo = ranksMap.get(dinerInfo.getId());
            userVo.setIsMe(1);
            return Lists.newArrayList(ranksMap.values());
        }
        /*获取排名*/
        Long myRank = redisTemplate.opsForZSet().reverseRank(
                RedisKeyConstant.diner_points.getKey(),
                dinerInfo.getId());
        if (myRank != null) {
            DinerPointsRankVO meVo = new DinerPointsRankVO();
            BeanUtil.copyProperties(dinerInfo, meVo);
            meVo.setIsMe(1);
            meVo.setRanks(myRank.intValue() + 1);//排名从1开始
            Double score = redisTemplate.opsForZSet().score(RedisKeyConstant.diner_points.getKey(), dinerInfo.getId());
            meVo.setTotal(score.intValue());//设置积分
            ranksMap.put(dinerInfo.getId(), meVo);
        }
        return Lists.newArrayList(ranksMap.values());

    }


    /*查询排行榜前20与登录用户的排名  MySQL版*/
    public List<DinerPointsRankVO> findDinerPointRank(String accessToken){

        SingInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);                       //获取用户登录信息
        List<DinerPointsRankVO> topnList = dinerPointsMapper.findTopN(TOPN);                //统计积分排行榜
        if(CollectionUtil.isEmpty(topnList)){
            return new ArrayList<>(1);
        }
        /* 判断用户是否在排行榜中，在追加标记,使用LinkedHashMap保证顺序 */
        Map<Integer,DinerPointsRankVO> resultMap = new LinkedHashMap<>(20);
        for (DinerPointsRankVO tempVo : topnList) {
            resultMap.put(tempVo.getId(), tempVo);
        }
        if(resultMap.containsKey(dinerInfo.getId())){
            DinerPointsRankVO loginVo = resultMap.get(dinerInfo.getId());
            loginVo.setIsMe(1);
            return Lists.newArrayList(resultMap.values());
        }
        /*登录用户不在排行榜，进行查询*/
        DinerPointsRankVO myRank = dinerPointsMapper.findDinerRank(dinerInfo.getId());
        myRank.setIsMe(1);
        topnList.add(myRank);
        return topnList;
    }

    /*获取登录信息*/
    private SingInDinerInfo loadSignInDinerInfo(String accessToken) {

        /*判断Token是否为空*/
        AssertUtil.mustLogin(accessToken);
        /*获取登录信息*/
        String url = oauthServerName+"user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if(resultInfo.getCode()!= ApiConstant.SUCCESS_CODE){
            throw new ParameterException(resultInfo.getMessage());
        }
        SingInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap)resultInfo.getData(),new SingInDinerInfo(),false);
        return dinerInfo;

    }


}
